package org.dreamwork.jasmine2.web.controls.datagrid;

import org.dreamwork.jasmine2.engine.IBindable;
import org.dreamwork.jasmine2.events.EventException;
import org.dreamwork.jasmine2.util.DataContainer;
import org.dreamwork.jasmine2.util.StringConverter;
import org.dreamwork.jasmine2.util.WebControlCollection;
import org.dreamwork.jasmine2.web.IWebContainer;
import org.dreamwork.jasmine2.web.IWebControl;
import org.dreamwork.jasmine2.web.controls.Literal;
import org.dreamwork.jasmine2.web.controls.Repeater;
import org.dreamwork.jasmine2.web.html.GenericHtmlControl;
import org.dreamwork.jasmine2.web.html.Table;
import org.dreamwork.jasmine2.web.html.TableCell;
import org.dreamwork.jasmine2.web.html.TableRow;
import org.dreamwork.peony.DataBinder;
import org.dreamwork.util.ICollection;
import org.dreamwork.util.StringUtil;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.math.BigDecimal;
import java.util.*;

/**
 * Created by IntelliJ IDEA.
 * User: seth.yang
 * Date: 2010-10-14
 * Time: 15:44:44
 */
public class DataGrid extends Table {
    List<GroupControl> groups = new ArrayList<GroupControl> ();
    List<Column> columns = new ArrayList<Column> ();
    Caption caption;

    private boolean dataSorted = false;
    private boolean showHeader = true, showFooter = true;

    private List<?> list;

    @Override
    @SuppressWarnings ("unchecked")
    public void onDataBind (Object sender) throws EventException {
        if (dataSource == null) return;
        list = DataBinder.parseDataSource (dataSource);
        sortData ();

        DataContainer container = new DataContainer ();
        if (page.container != null) {
            container.parent = page.container;
            page.stack.push (page.container);
        }
        page.container = container;

        createChildControls ();
    }

    @Override
    public void createChildControls () {
//        childControls.clear ();
        Collection<IWebControl> coll = new HashSet<IWebControl> ();
        coll.addAll (childControls.values ());
        for (IWebControl c : coll) {
            if (c instanceof Column) childControls.remove (c.getId ());
        }
        coll.clear ();
        coll = null;
//        if (caption != null) addControl (caption);

        // build grid header
        createHeader ();

        if (groups.size () > 0) {
            List<Group> gs = c ();
            for (Group g : gs) createGroupBody (g);
        } else {
            for (int i = 0; i < list.size (); i ++) {
                Object container = list.get (i);
                page.container.dataIndex = i;
                page.container.dataItem = container;
                createDataRow (container);
            }
        }

        childControlCreated = true;
    }

    public void addGroup (GroupControl group) {
        groups.add (group);
    }

    public void addColumn (Column column) {
        columns.add (column);
    }

    public Caption getCaption () {
        return caption;
    }

    public void setCaption (Caption caption) {
        this.caption = caption;
    }

    public boolean isDataSorted () {
        return dataSorted;
    }

    public void setDataSorted (boolean dataSorted) {
        this.dataSorted = dataSorted;
    }

    public boolean isShowHeader () {
        return showHeader;
    }

    public void setShowHeader (boolean showHeader) {
        this.showHeader = showHeader;
    }

    public boolean isShowFooter () {
        return showFooter;
    }

    public void setShowFooter (boolean showFooter) {
        this.showFooter = showFooter;
    }

    @SuppressWarnings ("unchecked")
    private void sortData () {
        if (!dataSorted && groups.size () > 0) {
            GroupControl main = groups.get (groups.size () - 1);
            Comparator c = main.getComparator ();
            Collections.sort (list, c);
            if (groups.size () > 1) for (int i = groups.size () - 2; i >= 0; i--) {
                GroupControl group = groups.get (i);
                Collections.sort (list, group.getComparator ());
            }
        }
    }

    private void createHeader () {
        if (columns.size () > 0) {
            GenericHtmlControl thead = new GenericHtmlControl ("thead");
            TableRow tr = new TableRow ();
            tr.setAttribute ("class", id + "-grid-header");
            thead.addControl (tr);

            for (Column column : columns) {
                TableCell th = new TableCell (true);
                th.setId (id + "-" + column.getId () + "-column");
                for (Iterator i = column.getAttributeNames (); i.hasNext ();) {
                    String attrName = (String) i.next ();
                    String attrValue = column.getAttribute (attrName);
                    th.setAttribute (attrName, attrValue);
                }
                for (Iterator i = column.getDynamicAttributeNames (); i.hasNext ();) {
                    String attrName = (String) i.next ();
                    String attrValue = column.getDynamicAttribute (attrName);
                    th.setDynamicAttribute (attrName, attrValue);
                }
                th.setInnerText (column.getHeadText ());
                tr.addCell (th);
            }
            thead.addControl (tr);
            addControl (thead);
        }
    }

    private void createGroupBody (Group g) {
        if (g.position == Position.Top) createGroupSummary (g);
        for (Group sg : g.subGroup) createGroupBody (sg);
        for (Object o : g.data) createDataRow (o);
        if (g.position == Position.Bottom) createGroupSummary (g);
    }

    private void createGroupSummary (Group group) {
        TableRow tr = new TableRow ();
//        String spe = "";
        String classId = id + "-" + group.summaryValue;
        tr.setAttribute ("class", classId);

        TableCell td = new TableCell ();
/*
        String tdId = id + "-td" + spe;
        td.setId (tdId);
*/
        td.setAttribute ("colspan", String.valueOf (columns.size ()));

        GenericHtmlControl groupTitle = new GenericHtmlControl ("div");
        groupTitle.setAttribute ("class", "group-title");
        String title = group.name;
        if (StringUtil.isEmpty (title)) title = StringConverter.contvertToString (group.value);
        groupTitle.addControl (new Literal (title));
        td.addControl (groupTitle);

        GenericHtmlControl groupSummary = new GenericHtmlControl ("div");
        groupSummary.setAttribute ("class", "group-summary");
        groupSummary.addControl (new Literal (StringConverter.contvertToString (group.getSummaryValue ())));
        td.addControl (groupSummary);

        tr.addCell (td);
        addRow (tr);
    }

    private void createDataRow (Object container) {
        TableRow tr = new TableRow ();
        for (Column column : columns) {
            TableCell td = new TableCell ();
            for (Iterator i = column.getAttributeNames (); i.hasNext ();) {
                String name = (String) i.next ();
                td.setAttribute (name, column.getAttribute (name));
            }
            if (!StringUtil.isEmpty (column.getValueField ())) {
                Object value = DataBinder.eval (container, column.getValueField ());
                String text;
                if (!StringUtil.isEmpty (column.getFormat ()))
                    text = StringConverter.format (value, column.getFormat ());
                else
                    text = value.toString ();
                td.setInnerText (text);
            } else if (column.getChildControls ().count () > 0) {
                ICollection col = column.getChildControls ();
                for (int i = 0; i < col.count (); i ++)
                    try {
                        bindControl (td, (IWebControl) col.get (i));
                    } catch (Throwable throwable) {
                        throw new RuntimeException (throwable);
                    }
            } else {
                td.setInnerText ("&nbsp;");
            }
            tr.addCell (td);
        }
        addRow (tr);
    }

    private void bindControl (IWebContainer parent, IWebControl control) throws Throwable {
        for (Iterator iter = control.getDynamicAttributeNames (); iter.hasNext ();) {
            String attrName = (String) iter.next ();
            if (attrName.equalsIgnoreCase ("datasource")) continue;
            String expression = control.getDynamicAttribute (attrName);
            Object value = page.eval (expression);
            control.setAttribute (attrName, String.valueOf (value));
        }

        IWebControl copy = null;
        if (control instanceof Repeater) {
            Repeater repeater = (Repeater) ((Repeater)control).copy ();
            repeater.dataBind ();
            for (int w = 0; w < repeater.getChildControls ().count (); w++) {
                IWebControl c = (IWebControl) repeater.getChildControls ().get (w);
                parent.addControl (c);
            }
        } else if (control instanceof IWebContainer) {
            IWebContainer container = (IWebContainer) control;
            IWebContainer ret = (IWebContainer) container.copy ();
            WebControlCollection child = (WebControlCollection) container.getChildControls ();
            for (int i = 0; i < child.count (); i++) {
                bindControl (ret, child.getControl (i));
            }
            copy = ret;
        } else if (control instanceof IBindable) {
            control.setClientId (control.getId () + "_" + page.container.dataIndex);
            ((IBindable) control).dataBind ();
            StringWriter sw = new StringWriter ();
            control.render (new PrintWriter (sw));
            copy = new Literal (sw.getBuffer ());
        } else {
            copy = control.copy ();
            copy.setId (control.getId () + "_" + page.container.dataIndex);
        }
        if (copy != null)
            parent.addControl (copy);
    }

    private static class Group {
        String name;
        Object value;
        Object summaryValue;
        BigDecimal total = new BigDecimal ("0");
        GroupSummary summary;
        Position position;
        int count;
        List<Object> data = new ArrayList<Object> ();
        List<Group> subGroup = new ArrayList<Group> ();

        Object getSummaryValue () {
            if (summary == GroupSummary.Count) return count;
            else if (summary == GroupSummary.Sum) return total;
            return "";
        }

        void sum (Number number) {
            total = total.add (new BigDecimal (number.toString ()));
        }
    }

    private List<Group> c () {
        List<Group> ret = new ArrayList<Group> ();
        Map<String, Group> gs = new HashMap<String, Group> ();
        GroupControl lastGroup = groups.get (groups.size () - 1);
        for (Object container : list) {
            StringBuilder sb = new StringBuilder ();
            for (GroupControl gc : groups) {
                Object o = DataBinder.eval (container, gc.getBy ());
                Group parent = null;
                if (sb.length () > 0) {
                    parent = gs.get (sb.toString ());
                    sb.append ('.');
                }
                sb.append (o);

                Group g = gs.get (sb.toString ());
                if (g == null) {
                    g = new Group ();
                    g.name = gc.getTitle ();
                    g.position = gc.getPosition ();
                    g.value = o;
                    g.summary = gc.getSummary ();
                    g.summaryValue = gc.getBy ();
                    gs.put (sb.toString (), g);
                    if (parent != null) parent.subGroup.add (g);
                    if (parent == null) ret.add (g);
                }

                if (gc.getSummary () == GroupSummary.Sum) {
                    g.sum ((Number) DataBinder.eval (container, gc.getTo ()));
                } else {
                    g.count ++;
                }
                if (gc == lastGroup) g.data.add (container);
            }
        }
/*


        for (int i = 0 ; i < gs.count (); i ++) {
            Group g = gs.get (i);
            System.out.printf ("group { name = %s, value = %s, summary = %s%n", g.name, g.value, g.getSummaryValue ());                                                                                                     
            for (Object c : g.data) {
                System.out.println (c);
            }
        }

*/
        return ret;
    }
}